#include "CDXRenderWin32.h"
#include "IDXRenderable.h"
#include "CDXMeshManager.h"
#include "CDXTextureManager.h"
#include "CDXShaderManager.h"
#include "CDXLights.h"
#include "CDXCamera.h"
#include "CDXRenderToTexture.h"

#if _DEBUG
#define _LOGGER_
#endif
#include "Logger.h"



CDXRenderWin32 CDXRenderWin32::m_instance;

CDXRenderWin32::CDXRenderWin32()
{

}

CDXRenderWin32::~CDXRenderWin32()
{

}

bool CDXRenderWin32::Init(HWND hWndGameWindow, int nWidth, int nHeight, bool bIsSceneRenderedToTexture)
{

	//////////////////////////////////////////////////////////////////////////
	//Setting up the Device!
	//////////////////////////////////////////////////////////////////////////
	Logger::OutputToVS("CDXRenderWin32", "Initialization Started...");

	m_bIsSceneRenderedToTexture = bIsSceneRenderedToTexture;

	pD3d = Direct3DCreate9(D3D_SDK_VERSION);	

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));

	d3dpp.hDeviceWindow = hWndGameWindow;
	d3dpp.Windowed = true;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //no VSync
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

	// add for 3D
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	pD3d->CreateDevice(0, D3DDEVTYPE_HAL, hWndGameWindow,
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDXDevice);

	pDXDevice->SetRenderState(D3DRS_ZENABLE, true);
	pDXDevice->SetRenderState(D3DRS_LIGHTING, true);

	//Init Texture Manager
	m_pTextureManager = CDXTextureManager::GetInstance();
	m_pTextureManager->InitTextureManager(pDXDevice);
	Logger::OutputToVS("CDXRenderWin32", "CDXTextureManager Initialized");

	//Init Mesh Manager
	m_pMeshManager = CDXMeshManager::GetInstance();
	m_pMeshManager->Init("Meshes\\", pDXDevice);
	Logger::OutputToVS("CDXRenderWin32", "CDXMeshManager Initialized");

	// Init the camera and set the projection
	m_pCamera = new CDXCamera();
	pDXDevice->SetTransform(D3DTS_PROJECTION, &m_pCamera->CreateProjMatrixLH(D3DX_PI * 0.5f, // 90 - degrees
								(float)nWidth/(float)nHeight,
								1.0f, 1000.0f));

	if (m_bIsSceneRenderedToTexture)
	{
		Logger::OutputToVS("CDXRenderWin32", "SceneRenderedToTexture is Active");
		m_RenderToTexture = new CDXRenderToTexture();
		m_RenderToTexture->InitR2T();
	}
	else
	{
		Logger::OutputToVS("CDXRenderWin32", "SceneRenderedToTexture is Inactive");
		m_RenderToTexture = NULL;
	}

	Logger::OuputString("CDXRenderWin32", "Initialization Succeeded!");


	return true; //success
}

void CDXRenderWin32::Shutdown()
{
	Logger::OutputToVS("CDXRenderWin32", "Shutdown Started...");
	m_pMeshManager->Shutdown();
	m_pTextureManager->ShutdownTextureManager();

	if(m_pCamera != NULL)
	{
		delete m_pCamera;
		m_pCamera = NULL;
	}
	
	if (m_RenderToTexture)
	{
		delete m_RenderToTexture;
		m_RenderToTexture = NULL;
	}

	pDXDevice->Release();
	pD3d->Release();
	Logger::OuputString("CDXRenderWin32", "Shutdown Succeeded!");
}

void CDXRenderWin32::Render(const vector<IDXRenderable*>* drawList)
{
	//HACK ghetto camera
	//////////////////////////////////////////////////////////////////////////
	// Animate the camera:
	// The camera will circle around the center of the scene.  We use the
	// sin and cos functions to generate points on the circle, then scale them
	// by 10 to further the radius.  In addition the camera will move up and down
	// as it circles about the scene.
	//////////////////////////////////////////////////////////////////////////

	static float angle = (3.0f * D3DX_PI) / 2.0f;
	static float cameraHeight = 0.0f;
	static float cameraHeightDirection = 5.0f;

	m_pCamera->SetPosition( cosf(angle) * 10.0f, cameraHeight, sinf(angle) * 10.0f );

	// the camera is targeted at the origin of the world
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	pDXDevice->SetTransform(D3DTS_VIEW, &m_pCamera->LookAtLH(&target));

	// compute the position for the next frame
	angle += 0.001f;
	if( angle >= 6.28f )
		angle = 0.0f;

	// compute the height of the camera for the next frame
	cameraHeight += cameraHeightDirection * 0.001f;
	if( cameraHeight >= 10.0f )
		cameraHeightDirection = -5.0f;

	if( cameraHeight <= -10.0f )
		cameraHeightDirection = 5.0f;

	D3DXMATRIX trans;

	D3DXMatrixTranslation(&trans,  0.0f, 0.0f,  0.0f);

	//////////////////////////////////////////////////////////////////////////
	//HACK Light
	//////////////////////////////////////////////////////////////////////////
	light = CDXLights::GetInstance()->CreatePointLight(vec3f(0.0f, 1.0f, 1.0f));
	
	SetLights(0, &light);
	TurnLightsOn(0, true);
	//////////////////////////////////////////////////////////////////////////

	// Dereference the drawList passed in
	const std::vector<IDXRenderable*>& objList = (*drawList);
	unsigned drawListSize = (unsigned)objList.size();


	//SetRenderStateWireFrame(true);
	
	SetRenderStateWireFrame(false);
	if (m_bIsSceneRenderedToTexture)
	{
		m_RenderToTexture->StartR2T();
	}

	pDXDevice->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);

	pDXDevice->BeginScene();


		pDXDevice->SetTransform(D3DTS_WORLD, &trans);
		//mesh->DrawSubset(0);
		IDXMesh* curMesh;
		for (unsigned i = 0; i < drawListSize; ++i)
		{
			//////////////////////////////////////////////////////////////////////////
			//Jesus, I hope this works....
			//////////////////////////////////////////////////////////////////////////
			if (objList[i]->IsVisible())
			{
				if (objList[i]->GetRenderType() == IDXRenderable::DX_3DOBJECT)
				{
					if (objList[i]->IsShaderApplied())
					{
						DXSHADER* pTheShader = CDXShaderManader::GetInstance()->GetDXShader(objList[i]->GetShaderID());
						if (SUCCEEDED(pTheShader->theEffect->SetTechnique(pTheShader->hTech)))
						{
							UINT numPasses = 0;
							pTheShader->theEffect->Begin(&numPasses,0);

							for (UINT i = 0;i < numPasses; i++)
							{
								pTheShader->theEffect->BeginPass(i); // Set the pass

								curMesh = m_pMeshManager->GetMeshByID(objList[i]->GetRenderID());
								curMesh->DrawMesh(pDXDevice);
								curMesh = NULL;

								pTheShader->theEffect->EndPass();
							}

							pTheShader->theEffect->End();
						}
					}
					else
					{
						curMesh = m_pMeshManager->GetMeshByID(objList[i]->GetRenderID());
						curMesh->DrawMesh(pDXDevice);
						curMesh = NULL;
					}
				} 
				else if (objList[i]->GetRenderType() == IDXRenderable::DX_2DOBJECT)
				{
					//////////////////////////////////////////////////////////////////////////
					//TODO!!! 
					//		:Add shaders and rendering for 2d shit.
					//////////////////////////////////////////////////////////////////////////

				}
			}
		}
	pDXDevice->EndScene();
	//////////////////////////////////////////////////////////////////////////


	if (m_bIsSceneRenderedToTexture)
	{
		m_RenderToTexture->RenderPassR2T(trans);
		//pDXDevice->Present(NULL, NULL, NULL, NULL);

	}
	else
	{
		pDXDevice->Present(NULL, NULL, NULL, NULL);
	}

}

void CDXRenderWin32::Clear(D3DCOLOR clearColor, float Z, DWORD flags, DWORD numRects, const D3DRECT* rectArray, DWORD stencil)
{
	pDXDevice->Clear(numRects, rectArray, flags, clearColor, Z, stencil);
}


HRESULT CDXRenderWin32::DXCreateEffectFromFile(LPDIRECT3DDEVICE9 theDevice,LPCSTR srcFile,CONST D3DXMACRO* pDefines,LPD3DXINCLUDE pInclude,DWORD Flags,LPD3DXEFFECTPOOL pPool,LPD3DXEFFECT* ppEffect, LPD3DXBUFFER* ppErrors )
{
	HRESULT res;
	res = D3DXCreateEffectFromFile(theDevice,srcFile,pDefines,pInclude,Flags,pPool,ppEffect,ppErrors);
	return res;
}

HRESULT CDXRenderWin32::DXCreateTextureFromFile(LPDIRECT3DDEVICE9 theDevice,LPCSTR srcFile,UINT uWidth,UINT uHeight,
												UINT uMipLvls,DWORD dUsage,D3DFORMAT dFormat,D3DPOOL dPool,DWORD dFilter,
												DWORD dMipFilter,D3DCOLOR dColorKey,D3DXIMAGE_INFO* pSrcInfo,PALETTEENTRY* pPalette,
												LPDIRECT3DTEXTURE9* ppTexture)
{
	HRESULT res;
	res = D3DXCreateTextureFromFileEx(theDevice,srcFile,uWidth,uHeight,uMipLvls,dUsage,
		dFormat,dPool,dFilter,dMipFilter,dColorKey,pSrcInfo,pPalette,ppTexture);
	return res;
}

HRESULT CDXRenderWin32::SetRenderState(D3DRENDERSTATETYPE d3dRenderState, DWORD dwValue )
{
	HRESULT res;
	res = pDXDevice->SetRenderState(d3dRenderState, dwValue);
	return res;
}

void CDXRenderWin32::SetRenderStateBlending(bool bIsOn)
{
	if (bIsOn == false)
	{
		pDXDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
		pDXDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
	}
	else
	{
		pDXDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
		pDXDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
	}
	
}
void CDXRenderWin32::SetRenderStateWireFrame(bool bIsOn)
{
	if (bIsOn)
	{
		pDXDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	}
	else
	{
		pDXDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
	}

}

#if _DEBUG
#undef _LOGGER_
#endif
